////
//
/// \file ./test/operation_node_test.cpp
//
//  Copyright 2007, 2008 Chandler Carruth
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations
//  under the License.
//
////

#include <inc/parser/operation_node.hpp>

#include <string>

#include <gtest/gtest.h>

#include <inc/parser/utilities.hpp>

namespace inc {
namespace parser {

using std::string;

TEST(OperationNodeTest, parse_double_label)
{
  const string data[] = { "( foo bar )", "(a b)", "( foo\n\t\tbar\n)" };
  for( int i = 0 ; i < elementsof( data ) ; ++i )
  {
    OperationNode node;
    string::const_iterator last_parsed;
    EXPECT_TRUE( node.parse( data[i].begin(), data[i].end(), last_parsed ) )
        << "Failed to parse: '" << data[i] << "'";
    EXPECT_EQ( data[i], string( data[i].begin(), last_parsed ) );
    EXPECT_EQ( data[i], node.get_text() );
    EXPECT_TRUE( node.is_double_label() )
        << "Not a double label: '" << data[i] << "'";
  }
}

TEST(OperationNodeTest, parse_unary_prefix_label)
{
  const string data[] = { "( not 0 )", "(bang 42)", "( not\n\t\t@bar\n)" };
  for( int i = 0 ; i < elementsof( data ) ; ++i )
  {
    OperationNode node;
    string::const_iterator last_parsed;
    EXPECT_TRUE( node.parse( data[i].begin(), data[i].end(), last_parsed ) )
        << "Failed to parse: '" << data[i] << "'";
    EXPECT_EQ( data[i], string( data[i].begin(), last_parsed ) );
    EXPECT_EQ( data[i], node.get_text() );
    EXPECT_TRUE( node.is_unary() )
        << "Not a unary op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_unary_prefix() )
        << "Not a prefix op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_op_label() )
        << "Not a label op: '" << data[i] << "'";
  }
}

TEST(OperationNodeTest, parse_unary_postfix_label)
{
  const string data[] = { "( 2 doubled )", "(4 u)", "( @foo\n\t\treversed\n)" };
  for( int i = 0 ; i < elementsof( data ) ; ++i )
  {
    OperationNode node;
    string::const_iterator last_parsed;
    EXPECT_TRUE( node.parse( data[i].begin(), data[i].end(), last_parsed ) )
        << "Failed to parse: '" << data[i] << "'";
    EXPECT_EQ( data[i], string( data[i].begin(), last_parsed ) );
    EXPECT_EQ( data[i], node.get_text() );
    EXPECT_TRUE( node.is_unary() )
        << "Not a unary op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_unary_postfix() )
        << "Not a postfix op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_op_label() )
        << "Not a label op: '" << data[i] << "'";
  }
}

TEST(OperationNodeTest, parse_unary_prefix_symbol)
{
  const string data[] = { "( !0 )", "(++x)", "( ~\n\t\t@bar\n)", "(~@x)" };
  for( int i = 0 ; i < elementsof( data ) ; ++i )
  {
    OperationNode node;
    string::const_iterator last_parsed;
    EXPECT_TRUE( node.parse( data[i].begin(), data[i].end(), last_parsed ) )
        << "Failed to parse: '" << data[i] << "'";
    EXPECT_EQ( data[i], string( data[i].begin(), last_parsed ) );
    EXPECT_EQ( data[i], node.get_text() );
    EXPECT_TRUE( node.is_unary() )
        << "Not a unary op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_unary_prefix() )
        << "Not a prefix op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_op_symbol() )
        << "Not a symbol op: '" << data[i] << "'";
  }
}

TEST(OperationNodeTest, parse_unary_postfix_symbol)
{
  const string data[] = { "( 42! )", "(x--)", "( @bam\n\t\t++\n)", "(@x!)" };
  for( int i = 0 ; i < elementsof( data ) ; ++i )
  {
    OperationNode node;
    string::const_iterator last_parsed;
    EXPECT_TRUE( node.parse( data[i].begin(), data[i].end(), last_parsed ) )
        << "Failed to parse: '" << data[i] << "'";
    EXPECT_EQ( data[i], string( data[i].begin(), last_parsed ) );
    EXPECT_EQ( data[i], node.get_text() );
    EXPECT_TRUE( node.is_unary() )
        << "Not a unary op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_unary_postfix() )
        << "Not a postfix op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_op_symbol() )
        << "Not a symbol op: '" << data[i] << "'";
  }
}

TEST(OperationNodeTest, parse_binary_label)
{
  const string data[] = { "( A and B )", "(2 x 2)", "( @foo\n\tor\t@bar\n)" };
  for( int i = 0 ; i < elementsof( data ) ; ++i )
  {
    OperationNode node;
    string::const_iterator last_parsed;
    EXPECT_TRUE( node.parse( data[i].begin(), data[i].end(), last_parsed ) )
        << "Failed to parse: '" << data[i] << "'";
    EXPECT_EQ( data[i], string( data[i].begin(), last_parsed ) );
    EXPECT_EQ( data[i], node.get_text() );
    EXPECT_TRUE( node.is_binary() )
        << "Not a binary op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_op_label() )
        << "Not a label op: '" << data[i] << "'";
  }
}

TEST(OperationNodeTest, parse_binary_symbol)
{
  const string data[] = { "( 1 + 1 )", "(2^2)", "( x\n\t*\ty\n)" };
  for( int i = 0 ; i < elementsof( data ) ; ++i )
  {
    OperationNode node;
    string::const_iterator last_parsed;
    EXPECT_TRUE( node.parse( data[i].begin(), data[i].end(), last_parsed ) )
        << "Failed to parse: '" << data[i] << "'";
    EXPECT_EQ( data[i], string( data[i].begin(), last_parsed ) );
    EXPECT_EQ( data[i], node.get_text() );
    EXPECT_TRUE( node.is_binary() )
        << "Not a binary op: '" << data[i] << "'";
    EXPECT_TRUE( node.is_op_symbol() )
        << "Not a symbol op: '" << data[i] << "'";
  }
}


//
//  compound expression tests
//

#if 0
MY_TEST_EXPR_BINARY_SYMBOL( ss_imm_imm, "( 2 + 2 )" );
MY_TEST_EXPR_BINARY_SYMBOL( s_imm_imm,  "( 2+2 )" );
MY_TEST_EXPR_BINARY_SYMBOL( imm_imm,    "(2+2)" );
MY_TEST_EXPR_BINARY_SYMBOL( ss_imm_lab, "( 2 + b )" );
MY_TEST_EXPR_BINARY_SYMBOL( s_imm_lab,  "( 2+b )" );
MY_TEST_EXPR_BINARY_SYMBOL( imm_lab,    "(2+b)" );
MY_TEST_EXPR_BINARY_SYMBOL( ss_lab_lab, "( a + b )" );
MY_TEST_EXPR_BINARY_SYMBOL( s_lab_lab,  "( a+b )" );
MY_TEST_EXPR_BINARY_SYMBOL( lab_lab,    "(a+b)" );
MY_TEST_EXPR_BINARY_SYMBOL( ss_lab_imm, "( a + 1 )" );
MY_TEST_EXPR_BINARY_SYMBOL( s_lab_imm,  "( a+1 )" );
MY_TEST_EXPR_BINARY_SYMBOL( lab_imm,    "(a+1)" );

MY_TEST_EXPR_BINARY_LABEL( ss_imm_imm, "( 2 foo 2 )" );
MY_TEST_EXPR_BINARY_LABEL( imm_imm,    "(2 foo 2)" );
MY_TEST_EXPR_BINARY_LABEL( ss_imm_lab, "( 2 foo b )" );
MY_TEST_EXPR_BINARY_LABEL( imm_lab,    "(2 foo b)" );
MY_TEST_EXPR_BINARY_LABEL( ss_lab_lab, "( a foo b )" );
MY_TEST_EXPR_BINARY_LABEL( lab_lab,    "(a foo b)" );
MY_TEST_EXPR_BINARY_LABEL( ss_lab_imm, "( a foo 1 )" );
MY_TEST_EXPR_BINARY_LABEL( lab_imm,    "(a foo 1)" );
MY_TEST_NODE_FAIL( lab_s_imm_imm, "( 2foo2 )" );
MY_TEST_NODE_FAIL( lab_s_imm_lab, "( 2foob )" );
MY_TEST_NODE_FAIL( lab_s_lab_lab, "( afoob )" );
MY_TEST_NODE_FAIL( lab_s_lab_imm, "( afoo1 )" );

//  names with internal expressions
MY_TEST_NAME_INDICES( idx_1_1, "foo[ (2 + 2) ]", 1 );
MY_TEST_NAME_INDICES( idx_1_2, "foo[ (a+b) ]", 1 );
MY_TEST_NAME_INDICES( idx_1_3, "foo[(++x)]", 1 );
MY_TEST_NAME_INDICES( idx_2_1, "foo[(++x),(++y)]", 2 );
MY_TEST_NAME_INDICES( idx_2_2, "foo[(x+y),(x*y)]", 2 );
MY_TEST_NAME_INDICES( idx_4_1, "foo[(x+y),(x*y),bar(),@baz]", 4 );

//  functions with full(er) expressions as paramaters
MY_TEST_FUNC_CALL( f_call_1_1, "foo( (2 + 2) )", 1 );
MY_TEST_FUNC_CALL( f_call_1_2, "foo( (a+b) )", 1 );
MY_TEST_FUNC_CALL( f_call_1_3, "foo((++x))", 1 );
MY_TEST_FUNC_CALL( f_call_2_1, "foo((++x),(++y))", 2 );
MY_TEST_FUNC_CALL( f_call_2_2, "foo((x+y),(x*y))", 2 );
MY_TEST_FUNC_CALL( f_call_4_1, "foo((x+y),(x*y),bar(),@baz)", 4 );

//  simple, deep operator recursion
MY_TEST_UNARY( "( ++8 )", 1, prefix );
MY_TEST_UNARY( "( --( ++8 ) )", 2, prefix );
MY_TEST_UNARY( "(!(--(++8)))", 3, prefix );
MY_TEST_UNARY( "(not (quite (lisp (yet 8))))", 4, prefix );

MY_TEST_UNARY( "( 8++ )", 1, postfix );
MY_TEST_UNARY( "( (8++)-- )", 2, postfix );
MY_TEST_UNARY( "(((8++)--)!)", 3, postfix );
MY_TEST_UNARY( "((((8 is) definately) not) lisp)", 4, postfix );

MY_TEST_BINARY( "( ( a + b ) + c )", "( a + ( b + c ) )", 2 );
MY_TEST_BINARY( "(((a+b)+c)-x)", "(x-(a+(b+c)))", 3 );
MY_TEST_BINARY( "((((a+b)+c)-x) x foo)", "(foo x (x-(a+(b+c))))", 4 );
MY_TEST_BINARY( "(((((a+b)+c)-x) x foo)^4)", "(4^(foo x (x-(a+(b+c)))))", 5 );
#endif

} //  end parser namespace
} //  end inc namespace
